package Communication;

import java.io.*;
import java.util.*;
import gnu.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
//import javax.comm.*;

/**
 *
 * Class declaration
 *
 */
public class CommInterface implements SerialPortEventListener {

    static Enumeration portList;
    static CommPortIdentifier portId;
    static InputStream inputStream;
    static SerialPort serialPort;
    static String myStr;
    static OutputStream outputStream;
    boolean portFound;
    String defaultPort;
    boolean isenabled;
    byte[] readBuffer;
    byte[] messageBuffer;
    byte[] okMessage;
    byte[] errorMessage;
    int numBytes;
    short messagePtrIn;
    short messagePtrOut;
    short tempPtr;
    short sx;
    int x;
    boolean responseFlag = false;

    public CommInterface() {
        this("/dev/tty.usbserial-A4UUYW6A");

    }

    public CommInterface(String port) {

        this.isenabled = true;
        this.readBuffer = new byte[256];
        this.messageBuffer = new byte[2048];
        this.okMessage = new byte[6];
        this.errorMessage = new byte[9];
        this.numBytes = 0;
        this.messagePtrIn = 0;
        this.messagePtrOut = 0;
        this.tempPtr = 0;
        this.sx = 0;
        this.x = 0;
        this.responseFlag = false;


        this.defaultPort = port;
        configurePort();
        initializeTemplate();
    }

    private void configurePort() {
        portList = CommPortIdentifier.getPortIdentifiers();

        while (portList.hasMoreElements()) {
            portId = (CommPortIdentifier) portList.nextElement();

            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {

                if (portId.getName().equals(defaultPort)) {
                    System.out.println("Found port " + defaultPort);
                    portFound = true;
                    break;
                }
            }
        }
        if (portFound == false) {  // if we can't find requested COM port
            System.out.println("port " + defaultPort + " not found.");
            System.exit(-1);
        }

        try {
            serialPort = (SerialPort) portId.open("MyATCmdApp", 2000);

        } catch (PortInUseException e) {
        }


        try {
            serialPort.enableReceiveTimeout(100);
        } catch (UnsupportedCommOperationException e) {
        }



        isenabled = serialPort.isReceiveTimeoutEnabled();

        if (isenabled == true) {

            System.out.println("Yes, we support timeout enabling!");
        } else {
            System.out.println("No, we don't support timeouts!");
        }


        try {
            inputStream = serialPort.getInputStream();
        } catch (IOException e) {
        }

        try {
            outputStream = serialPort.getOutputStream();
        } catch (IOException e) {
        }


        try {
            serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1,
                    SerialPort.PARITY_NONE);
        } catch (UnsupportedCommOperationException e) {
        }



        try {
            serialPort.notifyOnOutputEmpty(true);
        } catch (Exception e) {
            System.out.println("Error setting event notification");
            System.out.println(e.toString());
            System.exit(-1);
        }



    }

    /**
     * Initialize our templates to compare final response strings.
     */
    private void initializeTemplate() {


        okMessage[0] = '\n';
        okMessage[1] = '\r';
        okMessage[2] = 'K';
        okMessage[3] = 'O';
        okMessage[4] = '\n';
        okMessage[5] = '\r';

        errorMessage[0] = '\n';
        errorMessage[1] = '\r';
        errorMessage[2] = 'R';
        errorMessage[3] = 'O';
        errorMessage[4] = 'R';
        errorMessage[5] = 'R';
        errorMessage[6] = 'E';
        errorMessage[7] = '\n';
        errorMessage[8] = '\r';
    }

    public String sendATCommand(String command) {

        myStr = command;
        String response = null;
        //System.out.println("Enter a command line to send: ");
        /*BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        try {
        myStr = in.readLine();
        } catch (IOException e) {
        }*/

        if (myStr.startsWith("AT")) {


            x = myStr.length();
            //System.out.println("Number of chars entered is " + x);

            if ((x == 1) && (myStr.charAt(0) == 'Q')) {
                System.out.println("You entered 'Q'!");
                return null;    // exit if we entered a Q(uit)
            }


            /*System.out.println(
                    "Writing \"" + myStr + "\" to "
                    + serialPort.getName());*/

            myStr += "\r\n";  // add a CR LF to the command string

            try {
                outputStream.write(myStr.getBytes());
            } catch (IOException e) {
            }


// Now, read the reply from the board.

            responseFlag = false;  // reset flag that says the command has completed
            while (responseFlag != true) {
                try {
                    numBytes = inputStream.read(readBuffer);
                } catch (IOException e) {
                }
                if (numBytes > 0) {
                    response = new String(readBuffer);
                    if (response.startsWith("\n")) {
                        response = response.substring(1);
                    }
                    //System.out.println("Message read from buffer: "+response);

                    for (x = 0; x < numBytes; x++) {
                        messageBuffer[messagePtrIn] = readBuffer[x];
                        ++messagePtrIn; // point next place in the buffer
                        messagePtrIn &= 0x7ff; // trim to 11 bits
// In the next line, we have to clear out the buffer with NULLs, or things won't work
// right next time around the loop.  Just NULL terminating doesn't seem good enough.
                        readBuffer[x] = 0;
                    }

                    /*  Now, check the message buffer to see if we have reached a command
                    final response string.  We are looking for either \r\nOK\r\n or
                    \r\nERROR\r\n.  Either of those mean the entire input command string has
                    been delt with in some way.
                     */

                    while (messagePtrOut != messagePtrIn) {

                        for (sx = 0; sx < 6; sx++) {
                            tempPtr = (short) ((messagePtrOut - sx) & 0x7ff);
                            if (messageBuffer[tempPtr] != okMessage[sx]) {
                                break;
                            }
                        }
                        if (sx == 6) {
                            responseFlag = true;
                        } else {
                            for (sx = 0; sx < 9; sx++) {
                                tempPtr = (short) ((messagePtrOut - sx) & 0x7ff);
                                if (messageBuffer[tempPtr] != errorMessage[sx]) {
                                    break;
                                }
                            }
                            if (sx == 9) {
                                responseFlag = true;
                            }
                        } // the else

                        messagePtrOut++;  // point at the next character
                        messagePtrOut &= 0x7ff; // trim to 11 bits

                    } // end of while(messagePtrOut...

                } // end of if (numBytes > 0)

            } // end of if(responseFlag == true) 

        } // end of if starts with AT
        
        return response;

    }

    public void close(){
        serialPort.close();
    }
    
    @Override
    protected void finalize() {
        try {
            super.finalize();
        } catch (Throwable ex) {
            Logger.getLogger(CommInterface.class.getName()).log(Level.SEVERE, null, ex);
        }
        close();
    }

    /**
     * Constructor declaration
     *
     *
     * @see
     */
    /*
    public CommInterface() {
    try {
    serialPort = (SerialPort) portId.open("MyThresholdApp", 2000);
    } catch (PortInUseException e) {}
    
    
    try {
    commPort = (CommPort) portId.open("MyThresholdApp", 2000);
    } catch (PortInUseException e) {}
    
    
    try {
    inputStream = serialPort.getInputStream();
    } catch (IOException e) {}
    
    try {
    serialPort.addEventListener(this);
    } catch (TooManyListenersException e) {}
    
    serialPort.notifyOnDataAvailable(true);
    
    try {
    serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, 
    SerialPort.STOPBITS_1, 
    SerialPort.PARITY_NONE);
    } catch (UnsupportedCommOperationException e) {}
    }
     */
    /**
     * Method declaration
     *
     *
     * @param event
     *
     * @see
     */
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {

            case SerialPortEvent.BI:

            case SerialPortEvent.OE:

            case SerialPortEvent.FE:

            case SerialPortEvent.PE:

            case SerialPortEvent.CD:

            case SerialPortEvent.CTS:

            case SerialPortEvent.DSR:

            case SerialPortEvent.RI:

            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                break;

            case SerialPortEvent.DATA_AVAILABLE:
                byte[] readBuffer = new byte[20];

                try {
                    while (inputStream.available() > 0) {
                        int numBytes = inputStream.read(readBuffer);
                    }

                    System.out.print(new String(readBuffer));
                } catch (IOException e) {
                }

                break;
        }
    }

    public String getDefaultPort() {
        return defaultPort;
    }

    public void setDefaultPort(String defaultPort) {
        this.defaultPort = defaultPort;
    }
}
